package edu.luc.cs.trull.task;

import java.beans.PropertyChangeEvent;

import EDU.oswego.cs.dl.util.concurrent.Executor;
import EDU.oswego.cs.dl.util.concurrent.WaitableInt;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
import org.apache.log4j.Logger;

import edu.luc.cs.trull.TerminatingComponent;


/**
 * A Worker for the step-by-step execution of a Task using an Executor.
 */
abstract class AbstractTaskWorker extends TerminatingComponent {

  private static Logger logger = Logger.getLogger(AbstractTaskWorker.class);

  // do not change: STOPPED < RUNNING < SUSPENDED
  protected static final int STOPPED   = 1;
  protected static final int RUNNING   = 2;
  protected static final int SUSPENDED = 3;

  protected Task task;

  protected Runnable start;

  protected Runnable finish = new Runnable() {
    public void run() {
      logger.debug(this + ".worker.run() requesting termination");
      scheduleTermination();
      logger.debug(this + ".worker.run() returning");
    }
  };

  protected final WaitableInt state = new WaitableInt(STOPPED);

  protected final SynchronizedBoolean firstTime = new SynchronizedBoolean(true);

  public AbstractTaskWorker(final Task task) {
    if (task == null) {
      throw new IllegalArgumentException();
    }
    this.task = task;
  }

  protected abstract Executor getExecutor();

  protected synchronized void setStart(Runnable start) {
    this.start = start;
  }
  
  public synchronized void setTask(Task task) {
    this.task = task; 
  }

  public void start(PropertyChangeEvent incoming) {
    logger.debug(this + ".start(" + incoming + ")");
    try {
      if (! state.commit(STOPPED, RUNNING)) {
        return;
      }
      getExecutor().execute(start);
      logger.debug(this + ".executor.execute() invoked");
    } catch (InterruptedException e) {
      logger.debug(this + ".executor.execute() interrupted");
    }
  }

  public void stop() {
    logger.debug(this + ".stop()");
    state.set(STOPPED);
  }

  public void suspend() {
    logger.debug(this + ".suspend()");
    state.commit(RUNNING, SUSPENDED);
  }

  public void resume() {
    logger.debug(this + ".resume()");
    state.commit(SUSPENDED, RUNNING);
  }
}